/*
 * $Id: content.c 453 2008-04-28 22:09:29Z jasta $
 *
 * Copyright (C) 2008 Josh Guilfoyle <jasta@devtcg.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

#include "main.h"

#include <errno.h>

#include <libsoup/soup-uri.h>
#include <libsoup/soup-server.h>
#include <libsoup/soup-server-message.h>

#include "db.h"

/*****************************************************************************/

static char *request_to_file(const char *path)
{
	char *ret;
	char *end;
	guint64 id;
	sqlite3 *dbh;
	sqlite3_stmt *stmt;
	int rc;
	size_t pfx = sizeof("/content/") - 1;

	assert(path != NULL);

	if (strncmp(path, "/content/", pfx) != 0)
		return NULL;

	path += pfx;

	id = g_ascii_strtoull(path, &end, 10);

	if (path == end)
		return NULL;

	/* TODO: Don't connect to the database for each request, ugh! */
	if (db_open_again(&dbh) == TRUE)
	{
		rc = sqlite3_prepare(dbh, "SELECT filename FROM songs WHERE _id = ? AND pending_delete = 0",
		  -1, &stmt, NULL);
		assert(rc == SQLITE_OK);

		sqlite3_bind_int64(stmt, 1, (sqlite_int64)id);

		if ((rc = sqlite3_step(stmt)) == SQLITE_ROW)
			ret = g_strdup(sqlite3_column_text(stmt, 0));

		sqlite3_finalize(stmt);

		db_close_again(dbh);
	}
	else
	{
		g_error("Failed to open database connection");
		return NULL;
	}

	return ret;
}

#if 0
static void send_more_data(SoupMessage *msg, gpointer udata);

static void client_msg_failed(SoupMessage *msg, gpointer udata)
{
	FILE *fp = udata;

	printf("client_msg_failed(...)\n");

	g_signal_handlers_disconnect_by_func(msg, send_more_data, udata);

	fclose(fp);
}

static void send_more_data(SoupMessage *msg, gpointer udata)
{
	FILE *fp = udata;
	char buf[4096];
	size_t n;

	n = fread(buf, 1, sizeof(buf), fp);

	if (n < sizeof(buf) && ferror(fp))
	{
		g_error("fread: %s", g_strerror(errno));
		soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
	}
	else if (n > 0)
	{
		soup_message_add_chunk(msg, SOUP_BUFFER_SYSTEM_OWNED,
		  g_memdup(buf, n), n);
		soup_message_io_unpause(msg);
	}

	/* Short read, either error or EOF, as handled above. */
	if (n < sizeof(buf))
	{
		soup_message_add_final_chunk(msg);
		soup_message_io_unpause(msg);

		printf("send_more_data() done...\n");

		g_signal_handlers_disconnect_by_func(msg, send_more_data, fp);
		g_signal_handlers_disconnect_by_func(msg, client_msg_failed, fp);
		fclose(fp);
	}
}
#endif

static unsigned char *slurp_file(FILE *fp, size_t *len)
{
	GString *data;
	unsigned char buf[1024];
	size_t n;

	data = g_string_new(NULL);
	assert(data != NULL);

	while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
		g_string_append_len(data, buf, n);

	if (ferror(fp))
	{
		*len = 0;
		g_string_free(data, TRUE);
		return NULL;
	}

	*len = data->len;
	return g_string_free(data, FALSE);
}

void content_serve_request(SoupServerContext *ctx, SoupMessage *msg,
  gpointer udata)
{
	char *path;
	char *filename;
	FILE *fp;
	unsigned char *data;
	size_t datalen;

	if (soup_method_get_id(msg->method) != SOUP_METHOD_ID_GET)
	{
		soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
		return;
	}

	path = soup_uri_to_string(soup_message_get_uri(msg), TRUE);
	assert(path != NULL);

	if ((filename = request_to_file(path)) == NULL)
	{
		g_error("Could not find suitable deliverable for request: %s", path);
		g_free(path);
		soup_message_set_status(msg, SOUP_STATUS_BAD_REQUEST);
		return;
	}

	g_free(path);

	if ((fp = fopen(filename, "r")) == NULL)
	{
		g_critical("fopen(%s): %s", filename, g_strerror(errno));
		g_free(filename);
		soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND);
		return;
	}

	printf("sending %s...\n", filename);

	/* XXX: This is VERY bad, but it seems this version of Soup doesn't
	 * offer a way to avoid it!? */
	data = slurp_file(fp, &datalen);
	assert(data != NULL);

	fclose(fp);

	g_free(filename);

	soup_message_set_status(msg, SOUP_STATUS_OK);

	soup_server_message_set_encoding(SOUP_SERVER_MESSAGE(msg),
	  SOUP_TRANSFER_CONTENT_LENGTH);

	soup_message_set_response(msg, "application/octet-stream",
	  SOUP_BUFFER_SYSTEM_OWNED, data, datalen);

#if 0
	soup_server_message_set_encoding(SOUP_SERVER_MESSAGE(msg),
	  SOUP_TRANSFER_CHUNKED);

	send_more_data(msg, fp);
	g_signal_connect(msg, "wrote_chunk", G_CALLBACK(send_more_data), fp);

	/* Disconnected once the last chunk is written; used only to catch
	 * intermediate disconnects. */
	g_signal_connect(msg, "finished", G_CALLBACK(client_msg_failed), fp);
#endif
}

/* Lame, destroy this entire function. */
void meta_serve_request(SoupServerContext *ctx, SoupMessage *msg,
  gpointer udata)
{
	char *path;
	char *pathptr;
	sqlite3 *dbh;

	if (soup_method_get_id(msg->method) != SOUP_METHOD_ID_GET)
	{
		soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
		return;
	}

	path = soup_uri_to_string(soup_message_get_uri(msg), TRUE);
	assert(path != NULL);

	g_message("path=%s", path);

	if (strncmp(path, "/meta/music/", 12) == 0)
	{
		char *end;
		char *sql;
		sqlite_int64 id;

		pathptr = path + 12;
		end = path;

		if (strncmp(pathptr, "album/", 6) == 0)
		{
			sql = "SELECT artwork_thumb FROM albums WHERE _id = ?";
			pathptr += 6;
		}
		else if (strncmp(pathptr, "artist/", 7) == 0)
		{
			sql = "SELECT photo_thumb FROM artists WHERE _id = ?";
			pathptr += 7;
		}
		else
			goto UNKNOWN_REQUEST;

		id = g_ascii_strtoull(pathptr, &end, 10);

		if (pathptr == end)
			goto UNKNOWN_REQUEST;

		/* TODO: Don't connect to the database for each request, ugh! */
		if (db_open_again(&dbh) == TRUE)
		{
			sqlite3_stmt *stmt;
			int rc;

			rc = sqlite3_prepare(dbh, sql, -1, &stmt, NULL);
			assert(rc == SQLITE_OK);

			sqlite3_bind_int64(stmt, 1, id);

			if ((rc = sqlite3_step(stmt)) == SQLITE_ROW)
			{
				const void *img = sqlite3_column_blob(stmt, 0);
				int imglen = sqlite3_column_bytes(stmt, 0);

				if (imglen > 0)
				{
					void *imgdup = g_memdup(img, (guint)imglen);

					soup_message_set_status(msg, SOUP_STATUS_OK);

					soup_server_message_set_encoding(SOUP_SERVER_MESSAGE(msg),
					  SOUP_TRANSFER_CONTENT_LENGTH);

					soup_message_set_response(msg, "application/octet-stream",
					  SOUP_BUFFER_SYSTEM_OWNED, imgdup, (gulong)imglen);
				}
				else
				{
					g_warning("No artwork for ID: %u", (unsigned int)id);
					soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND);
				}
			}
			else
			{
				g_warning("Requested unknown ID: %u", (unsigned int)id);
				soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND);
			}

			sqlite3_finalize(stmt);

			db_close_again(dbh);
		}
		else
		{
			g_error("Failed to open database connection");
			soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
			return;
		}
	}
	else
	{
UNKNOWN_REQUEST:
		g_warning("Unknown request: %s", path);
		soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND);
	}

	g_free(path);
}
